import scipy
import numpy
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

import Tkinter as Tk
import tkFileDialog
import tkSimpleDialog

from bEandI_classes_python_only import *
        
# read in some settings from the config file
synapse_parameters = from_config_string('synapse_parameters',config_file='config_EandI')
par_deltas = {} # a dictionary to hold the value each parameter should change on a +/- button hit
for par in synapse_parameters:
    par_deltas[par] = from_config(par,config_file='config_EandI')

# set up the cell object
c = Cell(config_filename='config_EandI')
c2 = Cell(config_filename='config_Ionly')

#--------------- MAIN GUI WINDOW -----------------
root = Tk.Tk()
root.wm_title("Fitting Synaptic Inputs     (By David Morton - August 2008)")
parameter_entries = []

#--------------- SIMULATE BUTTON -----------------
def set_params_and_run(run=True):
    """ this function sets the parameters to the values in the entry windows and
            runs the simulation and updates the plot"""
    # set the parameter values from the Entry fields
    for pe in parameter_entries:
        c.update_synapse(pe['name'],float(pe['frame/entry'][1].get()))
    # run the simulation and update the plots
    if run:
        c.exercise()
        c.plot_results()


#---------------- INCREASE/DECREASE BUTTONS --------
def increase_parameter(parameter_delta, entry_widget):
    """ This function will update the value of the parameter in the entry window. """
    current_value = float(entry_widget.get())
    new_value = current_value + parameter_delta
    entry_widget.delete(0,Tk.END)
    entry_widget.insert(0,str(new_value))

def decrease_parameter(parameter_delta,entry_widget):
    """ This function will update the value of the parameter in the entry window. """
    current_value = float(entry_widget.get())
    new_value = current_value - parameter_delta
    entry_widget.delete(0,Tk.END)
    entry_widget.insert(0,str(new_value))

def set_func(event):
    set_params_and_run(run=False)
    Fit_inhibition_only_dialog(root)

def parameter_display(parameter_name,parameter_delta, master=root, set_with_dialog=False):
    """ This method returns a pointer to the frame which holds:
            a Label
            a decrease value Button
            an Entry field
            an increase value Button
    """
    frame = Tk.Frame(master=master,height=35,width=100)
    label = Tk.Label(master=frame,text=parameter_name + ": ")
    entry = Tk.Entry(master=frame)

    if set_with_dialog:
        set_button = Tk.Button(master=frame,text='Set')
        set_button.bind('<Button-1>',set_func)
        # set up the grid placement
        label.grid(row=1,column=1)
        entry.grid(row=1,column=3)
        entry.config(state='readonly')
        set_button.grid(row=1,column=4)
    else:
        iv_button = Tk.Button(master=frame,text="+")
        ip = lambda e:increase_parameter(parameter_delta,entry)
        iv_button.bind('<Button-1>',ip)
        dv_button = Tk.Button(master=frame,text="-")
        dp = lambda e:decrease_parameter(parameter_delta,entry)
        dv_button.bind('<Button-1>',dp)
        # set up the grid placement
        label.grid(row=1,column=1)
        dv_button.grid(row=1,column=2)
        entry.grid(row=1,column=3)
        iv_button.grid(row=1,column=4)
    return [frame,entry]


class Fit_inhibition_only_dialog(tkSimpleDialog.Dialog):
    #--------------- SIMULATE BUTTON -----------------
    def set_params_and_run(self):
        """ this function sets the parameters to the values in the entry windows and
                runs the simulation and updates the plot"""
        # set the parameter values from the Entry fields
        c2.synapses[0].onset = float(self.parameter_entries[0]['frame/entry'][1].get())
        c2.synapses[0].tau = float(self.parameter_entries[1]['frame/entry'][1].get())
        c2.synapses[0].gmax = float(self.parameter_entries[2]['frame/entry'][1].get())
        c2.synapses[0].rev = float(self.parameter_entries[3]['frame/entry'][1].get())
        # run the simulation and update the plots
        c2.exercise()
        c2.plot_results()



    def body(self,master):
        self.canvas = FigureCanvasTkAgg(fig2,master=master)
        self.parameter_entries = []
        # values to put in the +/- lists
        # I_onset
        self.parameter_entries.append({'name':'I_onset',\
                                  'frame/entry':parameter_display('I_onset',1,master=master)})
        # I_tau
        self.parameter_entries.append({'name':'I_tau',\
                                  'frame/entry':parameter_display('I_tau',1,master=master)})
        # I_gmax
        self.parameter_entries.append({'name':'I_gmax',\
                                  'frame/entry':parameter_display('I_gmax',0.01,master=master)})
        # I_rev
        self.parameter_entries.append({'name':'I_rev',\
                                  'frame/entry':parameter_display('I_rev',1,master=master)})
        # set initial values in the entry boxes
        for pe in self.parameter_entries:
            pe['frame/entry'][1].insert(0,c2.get_synapse_parameter(pe['name']))

        self.sim_button = Tk.Button(master=master, text="Simulate", command=self.set_params_and_run)
    
        #-------------------- GRID PLACEMENT -----------------------------
        for i in range(len(self.parameter_entries)):
            self.parameter_entries[i]['frame/entry'][0].grid(row=i+1,column=1,sticky=Tk.E)
        self.sim_button.grid(row=30,column=1)
        self.canvas.get_tk_widget().grid(row=1,column=2,rowspan=30)
    
        return self.parameter_entries[0]['frame/entry'][1] # make the first entry the initial focus

    def apply(self):
        # this method gets called when the user selects ok or presses <ENTER>
        #   but not if they select cancel or press <ESC>

        # let's set the value of the reversal potential back in the main program.
        I_rev = self.parameter_entries[3]['frame/entry'][1].get()
        for pe in parameter_entries:
            if 'I_rev' in pe['name']:
                pe['frame/entry'][1].config(state=Tk.NORMAL)
                pe['frame/entry'][1].delete(0,Tk.END)
                pe['frame/entry'][1].insert(0,I_rev)
                pe['frame/entry'][1].config(state='readonly')

#---------------------OPEN/SAVE FILE BUTTONS-----------------------
def save_parameter_file():
    filename = tkFileDialog.asksaveasfilename(filetypes=[('Parameter Files','*.par')],title='Save as')
    ofile = open(filename,'w')
    ofile.write('# a parameter set for fit_synapses.py\n')

    ofile.write("synapse_names ")
    dict = {}
    for pe in parameter_entries:
        tokens = pe['name'].split('_')
        name_prefix = tokens[0]
        dict[name_prefix] = True
    for key in dict.keys():
        ofile.write('%s ' % key)
    ofile.write('\n')
    # output gnuplotable result
    c.gnuplot_print()
    
    for pe in parameter_entries:
        ofile.write("%s %s \n" % (pe['name'], pe['frame/entry'][1].get()))

def open_parameter_file(grid_placement=True):
    # get the filename from a filename dialog.
    filename = tkFileDialog.askopenfilename(filetypes=[('Parameter Files','*.par'),\
                    ('All Files','*')], title='Choose a parameter file.')

    # from the parameter file, build up the parameter_entries list.
    synapse_names = from_config_string('synapse_names',config_file=filename)

    # clear all the parameter_entries
    for pe in parameter_entries:
        pe['frame/entry'][0].grid_remove()
    
    for i in range(len(parameter_entries)):
        parameter_entries.pop()

    for name in synapse_names:
        for par in synapse_parameters:
            # amount the parameter will adjust when +/- button is hit
            par_delta = par_deltas[par]
            dict = {}
            dict['name'] = name + '_' + par
            if 'I_rev' in dict['name']:
                dict['frame/entry'] = parameter_display(dict['name'],par_delta,set_with_dialog=True)
            else:
                dict['frame/entry'] = parameter_display(dict['name'],par_delta)
            parameter_entries.append( dict )

    # last parameter entry is supposed to be set only by the IMC stim only fitting.
    
    for pe in parameter_entries:
        if 'I_rev' in pe['name']:
            pe['frame/entry'][1].config(state=Tk.NORMAL)
        pe['frame/entry'][1].delete(0,Tk.END)
        pe['frame/entry'][1].insert(0,from_config(pe['name'],config_file=filename))
        if 'I_rev' in pe['name']:
            pe['frame/entry'][1].config(state='readonly')

    #-------------------- GRID PLACEMENT -----------------------------
    if grid_placement:
        for i in range(len(parameter_entries)):
            parameter_entries[i]['frame/entry'][0].grid(row=i+1,column=1,sticky=Tk.E)
    c.set_synapses(filename) # automagically removes synapses if they were already set.
    return filename


parameter_filename = open_parameter_file() 
c2.set_synapses('Imc_only.par')

c.exercise()
c2.exercise()

# make the figure for displaying the results.
fig = c.plot_results()
fig2 = c2.plot_results()
canvas = FigureCanvasTkAgg(fig,master=root)

sim_button = Tk.Button(master=root, text="Simulate", command=set_params_and_run)
open_save_frame = Tk.Frame(master=root,height=35,width=100)
open_button = Tk.Button(master=open_save_frame,text='Open Parameter File',command=open_parameter_file)
save_button = Tk.Button(master=open_save_frame,text='Save Parameter File',command=save_parameter_file)
open_button.grid(row=1,column=1)
save_button.grid(row=1,column=2)


open_save_frame.grid(row=300,column=1)
sim_button.grid(row=301,column=1)
canvas.get_tk_widget().grid(row=1,column=2,rowspan=30)

Tk.mainloop()
